Okay, so the next section in our game I think we can script is the module script that will be responsible
for controlling the client's main guy.
This guy is going to display the objectives players need to complete.
It will display the intro message when we start the game.
The outro message when a player escapes or fails to escape, it will control the spectating system and
much more.
So to get started, let's go ahead and open up our main UI handler and start our player scripts inside
of here.
For now, we're only going to need four services, but that might change in the future as we continue
to add on more functionality to our main guy.
But first we need replicated storage.
The next service we'll need is the Tween Service.
We're going to need the player service.
And then we're going to need the sound service.
Now, from this point, we can go ahead and make the table that will represent this module script.
So we'll call it GUI handler and we'll make sure to return it at the end here.
And then some other variables we'll need for now is we first want to refer to our GUI actions enum.
So we'll require that for replicated storage and modules dot enums dot gui actions enum.
And they're also going to want to get our game comms event which is also in replicated storage dot events,
dot remotes, dot game communication.
And then we're also going to want to get the game comms enum for this particular event.
So we'll require remote storage, dot modules, dot enums, dot game communication enum.
And then the other event we're going to need is the update GUI event.
And that's in events dot remotes dot update guy.
Now we're also going to make a reference to the local player.
Because once we've gotten the local player, then we need to get our main guy.
Since this module script is not going to be a child of our main guy, or basically it is not a local
script, that's a child of the main guy.
We need to refer to this guy by accessing the local players dot player guy folder, and we need to wait
for this main guy to be replicated into this folder.
And then once we finally get the guy, then we can go ahead and reference all of the different frames
inside of it so we can make a variable for the death frame, for example.
So guy dot death frame we can make a reference to the message frame guy dot message frame.
And this is for the introduction message.
We're going to make a reference to the objective frame.
We'll just call it obj frame.
So guy dot objective frame.
And we'll want to make a reference to one of the example objectives that will appear in our objective
frame.
So we'll call it objective label.
And that's object frame dot objective label example.
We can also make a reference to our task frame.
So guy dot task frame we can make a reference to our outro frame.
Guitar outro frame, we can make a reference to the spectating frame guy.
Spectate frame.
We'll have a reference to a task label for our task frame as well, so we'll make it up here.
Task labels equal to task frame dot uh task label example.
We'll make a reference to the small little text label that will appear at the bottom of our screen.
So we'll call it small message label.
That's equal to give a small message label.
And then we'll make a reference to our countdown label.
Juiiet countdown label, and then we'll make a reference to that money frame.
Jewish money collection frame.
Now I'm going to create a table.
I'm going to call it messages.
And this table is going to store the messages we want to display onto the screen at the beginning of
the game.
That will fill up our message frame.
So that will be passed when we get the GUI action.
So let me pass in GUI actions enum and the action we want to do is the intro message.
So what are the messages going to be for the intro message?
Well, the first text in there is going to be the time which will be we'll do 11 p.m. the next message
will be something like on a bus to the Krusty Krab for your first shift.
This is to give some context to the players about what the game is about.
And then we can tell them their most important objective, which is get enough money to pay for rent.
And there we go.
Now I'm also going to create a new RNG data type.
And then I'm going to define two constants down here.
And these are going to be two colors.
So we're going to have a green color.
And we're also going to have a red color.
And these are the going to be the colors we're going to set for tasks and objective.
So when we fulfill a task we'll set the task color to green and then fade it out.
Or if we fail a task, we'll set it to red and then fade it out.
So for our green color we could do color three dot from RGB.
And then we could go in here and pick a nice green color, something like that.
And then we can do the same thing for the red color color three dot from RGB.
Open up our color wheel and pick some kind of red color like that.
All right.
Now some private functions we're going to need in here.
The first private function we'll need is for fading UI elements.
So instead of having to keep constantly using the tween service in our script multiple times to fade
in or fade out UI elements, we should just create one function that can do all of it for us.
So we're going to call this function fade element.
We'll get past a UI element to this function.
We'll get past the duration for the tween.
And then what transparency we want to set for this particular UI element.
And then inside of here what we're going to do is we're going to create a variable I'm going to call
it tween.
And what we're going to do is we're going to check what uh, class this UI element belongs to.
So if this UI element is of the class for example frame, then that means we need to change the background
transparency for this UI element.
So we'll create a new tween on this UI element.
We'll create a new tween info and pass the duration.
And then we're going to set the background transparency equal to the transparency pass to the function.
And then we can store this in our tween variable.
Another thing we can check is if this UI element is, for example, a text label.
So if it's a text label, then instead we need to fade out or fade in the text transparency instead.
So we can replace this with text transparency.
Let's say the UI element is a image label, right?
Maybe we need to fade out one of those.
Then we can do the exact same thing here.
But instead we need to fade out the image transparency.
Now, let's say the UI element is a UI stroke or basically the the outline that goes around specific
UI elements.
If it's UI stroke, then we need to create a new tween.
And instead on this stroke we just need to change its transparency property.
And then once we do that, we can check if tween is not nil, and if it isn't, then we can play our
tween.
And then we're going to have this, uh, function yield as well.
So we're going to do dot completed and we're going to wait for it to complete.
So just in case we need any functionality to wait for this tween to complete, we're going to do it
here.
But if we do not need to wait for this tween to complete, then we can simply just spawn this function
in a new thread and we can go ahead and ignore this, uh, yield statement right here.
Now, another private function we're going to need is for displaying the intro message so we can call
it display intro message.
Um, yeah, we'll just call it that display intro message.
And what will happen is we'll get past this table here containing all the messages, so we can call
it message table.
And then we'll also have a duration between the different messages.
So how long do we want to wait before we switch to the next message.
And then to the next message.
So we can call it delay between messages.
Another function we're going to need is for updating, uh, our tasks.
So we'll call it update task.
And we'll get passed some arguments to this function.
We may need a function to update all of our tasks instead of a specific task.
So we can call update all tasks.
And we'll get passed a task list or a list containing all of the tasks we need to update and put into
our task frame.
We'll have a function to fulfill a task, so we'll call it fulfill task.
We'll get passed arguments to this function as well from the server.
And then we'll want to have a function for updating all of the objectives.
So if we need to create new objectives then we'll do that.
So here uh we'll have a function for adding a new objective.
Will also get past, uh, the objective itself to this function.
We'll have a function to fulfill an objective.
We'll get past arguments to this as well.
And then we're also going to have the ability to fail an objective.
It passed arguments to this.
Another function we're going to need is for our small message label.
So we can call this function display small message.
And we would get past uh more arguments to this function as well.
Another function we're going to need is to display when we collect money.
So we can call this function display money frame.
We'll get passed arguments to this one.
We're also going to need a function for displaying our outro frame.
We'll also get passed arguments to this one from the server.
We're going to need a function for updating the countdown on our screen.
Also need args for this one.
And then we're also going to need a function for our spectating system.
So we'll call it start spectating.
And that will handle all of that kind of good stuff.
And lastly we'll need a private function for displaying when we have died so we can call it display
died frame.
All right.
So inside of the public function section we're going to create an initialize function for this.
And then we're also going to create a start function for this.
And this one's going to actually be very easy.
All we're going to do here in the start function is that we're going to get our objective frame, and
we're going to set the visibility equal to true.
So when the game starts we're automatically going to have the objectives displayed on top of our screen.
Now inside of the initialize section we're going to have to listen to our update guy event and get the
on client event and connect a Lambda function to this.
And we're going to get an action and argument supplied to that action from the server.
So that means we're going to have to listen to all of those different actions within our GUI actions
enum.
Now there's another neat way that we could do this by instead of having a bunch of if else if else if
statements for each of the different actions, we could actually create a table.
And I'm going to call this table case.
And this table is going to imitate something called a switch statement.
Now a switch statement is a very useful statement where it gets passed a condition and it only executes
a certain block of code based on whether or not that condition matches, you know, a particular section.
Now, unfortunately, Lua doesn't have a switch statement, and switch statements can be a lot more
performant than if else if else if statements.
But we can imitate a switch statement by using a dictionary.
So this dictionary can have a bunch of keys.
And we pass a key to the dictionary.
And then we execute a function that is connected to that key.
So for example let's say for the GUI actions enum of update objectives, we can have it reference our
update objectives function.
We could also do this for our GUI actions enum dot objective fulfilled.
We can have this reference are fulfilled uh fulfill objective function.
Oops, it looks like I named that wrongly.
Actually fixed that.
Fulfill objective.
We can do it for like guy actions enum dot objective failed and have that reference our failed objective
function.
Failed objective.
We can do this for, um, updating tasks.
So update tasks and we'll have this reference our update all task function.
We'll do the same thing for GUI actions enum dot update task and have that reference our update task
function.
We can do this for the GUI actions enum for let's say adding an objective.
So we'll reference the add objective function.
We'll do the same thing.
For, let's say, displaying a small message on the screen.
So display small message.
Oops.
Don't forget your commas.
And we're basically just going to keep going along and filling out our different DUI actions.
So for example, updating the countdown, we reference our update countdown function for UI actions
enum.
For example, let's say we want to hide the task frame.
So actions enum dot hide task frame.
This one's actually pretty easy.
We can just create a lambda function in here and then just do task frame dot visible equal to false.
We can do the GUI actions.
Enum dot collected money and again we'll have that reference our display money frame function.
We could do it for the guy.
Actions in um, outro frame, display, outro frame.
And then we could do it for the guy actions enum dot died and this one will actually be pretty simple.
Just create another Lambda function.
And what this will do is it's going to spawn our display died frame function in a new thread because
it's going to yield.
So we'll use defer to spawn display died frame in a new thread and we'll have it fade onto the screen
and stay faded on the screen for like around three seconds.
And then we're going to fade it out.
So that means we're going to have to wait for three seconds.
And then that means once our screen is completely covered by the death frame, then we can use our start
spectating function and display the spectating frame while our entire screen is blocked.
So that way you know it doesn't look awkward on the screen.
We're not going to immediately pop the spectating a frame onto the screen and switch the player's camera
around.
Instead, we're going to cover their screen with the died frame.
And then we're going to put the spectating screen on and then it fades out.
So that way it looks all smooth.
Now you may be wondering, well aren't we going to do one for the I believe it's the, um, intro message?
Don't you want to do it for this one?
And we're not going to do it for this one because we're going to be using a bind all event, because
our game handler here on the client is going to tell us when to exactly start displaying the intro messages.
So this is going to be reserved for a different event for now.
But other than that that means when our update GUI event gets fired, we get an action and the arguments
applied to the action, and we can literally just check if inside of our case table, the action that
the server wants us to do exists.
So if this action in the case table exists, then we can literally just do case action and then call
this function by putting two parentheses.
And then we can pass whatever arguments came from the server.
So we can just pass args here.
And that's all we need to do for our update UI event.
Now some other things that we're actually going to have to listen to in our initialize function is within
our spectate frame.
So there's a button in there called next which a player will click to go spectate the next player.
So we'll have to listen for when that is clicked.
But we're not going to fill out the functionality for this for now.
And then we have to do the same thing for a previous button in our spectating frame to go back to spectate
a previous player, and then also inside of our spectate frame, there is a respawn button and this
is the button the player will click to request to respawn, and they'll get prompts to purchase a developer
product.
And again, we're not going to fill out the functionality for this quite yet.
Now, what we can start doing is starting to fill out some of the functionality for these private functions.
So we can go ahead and start off, for example, with our update objectives function.
So inside of our update objectives function, we get passed arguments from the server.
And basically what we're going to do here is we're going to first loop through every single child inside
our objective frame dot objective list.
So there's a list in there that contains all of the objectives.
And we're going to get any children inside of this list.
And we're going to check if this child is a text label.
If it is then we need to destroy it because we're refreshing our objective list.
So we're going to get this child and destroy it.
Once we have cleared out the objective list, then what we're going to do is we're going to loop through
every single objective that got passed to us in that argument table, and we're going to create a new
clone of that, uh, objective label reference that we made.
So we're going to clone this.
We're going to set the name of the clone equal to the objective name.
We're going to set the text for the clone equal to the objective text.
And then we're going to set the parent of this clone equal to our objective frame dot objective list.
And then by default this clone is not going to be visible.
So we need to make sure to set the visibility of this clone equal to true.
So basically what's happening here is if I go to my main guy and I enable our objective frame here,
and I copy this objective label example and I paste it in here, make it visible, what's going to happen
is it's going to, uh, the name of this is going to change to whatever objective.
So for example, if it's the money objective, we'll change the name to money.
And the text will be something like, uh, earn $500 to pay for rent or something like that.
So that's one of the objectives that will pop up on the screen.
As you can see, it's got a nice border around it.
And then the text shows us exactly what we need to complete.
And if we get that objective fulfilled, then it's going to set this color to green and then fade it
out.
Or if we fail it, it'll set the color to red and then fade it out.
But that's basically what's going to be happening.
So let me go ahead and make this no longer visible.
And we can go back to our script.
The next function I guess we could go ahead and fill out is our add objective function.
So what we're going to do is we're first going to check inside of our objective frame dot objective
list.
We're going to see if we already have this objective inside of our objective frame.
So if uh objective frame objective list find first child and then pass the objective dot name if it
already exists in there, then we're just going to return out of this function.
Otherwise we're basically just going to do the exact same thing that we did here.
We're just going to clone the objective label, and we're going to set the name and text equal to this
objective.
Make sure to set the visibility to true.
And then what we're going to do is we're going to play a sound.
So inside of our sound service we have a folder called GUI which stores some guy sounds.
And we have an objective added sound.
So whenever a player gets an objective added, this sound is going to play.
And that way they know a new objective has been added to the game.
So we can reference this in sound service dot gui, dot objective added.
And we're just going to play the sound now.
I guess we can just go ahead and continue filling out these different functions.
So let's go ahead and fill out um our update all tasks function.
So this gets passed a list of all the tasks.
And we're basically just going to do the exact same thing that we did here.
But instead we're going to loop through our task frame, and inside of there there's a task list.
And we're going to make sure to destroy any previous children in there.
And then we're going to get every single task within our task list.
And then we're going to clone instead our task label.
Um, actually, let me just change this to tea, and then we could do tea.
Name?
Tea.
Text.
And then set the parent equal to the task frame dot task list.
And that looks pretty good there as well.
And I think actually what we'll also do is we'll make sure to enable the task frame when this happens.
So we'll set the visibility to true.
And then we're going to do the same thing for the objective frame just to make sure that it's all visible
on our screen.
Okay.
So the next function we can go ahead and fill out I think is our update task function.
So the purpose of this is to refer to an already existing task and update the value for this task.
So what we're going to do is we're going to make a variable in here.
We're going to call it which task.
And it's going to be equal to args.
And we'll have a key in this table called task to update.
So the server will tell us which task or the name of the task we need to update.
And then what we're going to do is we're going to get the text that's also supplied by the server in
this arguments table.
And now what we're going to do is we're going to find this task element inside of our task frame task
list.
And we're going to find first child.
Which task if this task does not exist for some reason.
Then we'll just create a new, uh, task element.
So we're basically just going to copy this here, create a clone and set the name equal to, um, which
task and then set the text equal to args.
Or actually we start it in the variable up here.
So we'll just set it equal to the text.
So just in case this for some reason this task that the server wants us to update does not exist.
We're going to quickly create it.
Otherwise, if it, uh, does exist, then we just need to do task element.
Dot text is equal to the text passed from the server.
I guess the next function we can go ahead and fill out is our display intro message function.
So what we're going to do here is we're going to loop through every single message inside of our message
table.
And where we're going to do is we're going to refer to our message frame, and we're going to set the
text in the text label that's in there equal to this particular message.
And then inside of the sound service there is another sound in here called boom.
So each time a new message is displayed we're going to play the sound pretty nice.
So we're going to do guy dot boom and then play the sound.
And then we're going to wait for the delay between each message.
So this is going to loop through every single message for example that we get past here.
It's going to play the boom sound three different times.
And then after that is over then we need to go ahead and fade out the guy.
So what we could do is we could loop through every single descendant.
Inside of our message frame.
So get descendants and we're going to do is we're going to fade out each of these items so we can do
task dot spawn.
And we're going to spawn our fade element and a new thread.
And we're going to pass to this the descendant.
We can fade it out after like let's say one second.
And we want to set the transparency to one.
So every single descendant and our message frame is going to be completely faded out.
And then what we could do is once the message frame or the intro message frame has been faded out,
then inside of the sound service, inside of the ambience folder, there's a folder called weather,
and then there's a rain sound and we're just going to play this sound.
So this sound is going to continually loop in our game because it's going to be raining.
And again, we're just adding more nice ambience into the game.
And then after that, we're going to fade.
Um, our message frame itself.
Because we didn't pass that inside of this loop, because it's looping through every single descendant,
but not the message frame itself.
So we need to make sure to also fan out the message frame.
And since this function is going to be yielding after it's finished yielding, then we can set the message
frame dot visibility equal to false.
And I think what would also be nice is that right, as we begin to fade out the message frame, we should
play another boom sound.
Perfect.
Okay.
So the next function we can go ahead and fill out is our fulfill task function.
So what do we want to do when we want to fulfill a task.
Well first we need to specify which task we want to fulfill.
So which task is equal to args dot fulfill task.
So the server again is going to provide to us which task has been fulfilled.
And then we're going to get the text passed to this which is args dot text.
And then what we're going to do is we're going to get this particular task.
So fulfilled task is equal to task frame dot task list.
We're going to find first child of this particular task that we need to fulfill.
If this fulfilled task exists.
So if a filled task.
Then we can set the fulfilled task.
Dot text.
Equal to the text passed to this function because as we are fulfilling tasks, for example, let's say
we completed two out of three toilets.
Well, once we finish three out of three toilets, then that means our task needs to be fulfilled,
but we still have to update the text.
Otherwise it'll still say something like two out of three instead of three out of three.
So then we update the text.
Then what we're going to do is we're going to get this fulfilled task, and we're going to set the text
color three equal to our green color.
And then what we could do is we could wait a random amount of time like four seconds.
So it'll stay on the screen for four seconds.
And then what we're going to do is we're going to fade element, which is going to be our um, task
itself.
So we'll just pass our fulfilled task.
Uh, will have this last something like one second, and then we'll set the transparency to one.
And then we're also going to want to do the same thing for the UI stroke that is going to be inside
of this task.
And what we could do is we could actually spawn that in a separate thread.
So we'll spawn our fade element function and a new thread.
We're going to pass fulfilled task dot UI stroke.
There's a UI stroke in there.
Again we're going to fade it out for one second and set the transparency to one.
And then after this is finished yielding right here, then we can just go ahead and destroy this fulfilled
task because we no longer need it.
All right.
Looks good.
Now we can basically just copy all of this and do the exact same thing for fulfilling an objective.
But instead we can, uh, do which objective, which is going to be equal to args.
And from the server we'll pass something like, uh, fulfilled objective name.
We could do something like that.
We'll get, uh, which objective we need to update.
We don't need text here for this objective though.
And then we can get the fulfilled.
Objective, and then we can go ahead and look inside of our objective frame dot objective list and find
first child which matches the objective name.
And then if this fulfilled objective exists.
Then we can delete this here.
Will set the fulfilled objective equal to the green color.
We'll wait four seconds again, and then we will fade out the UI stroke in this fulfilled objective
as well, and then fade out the fulfilled objective itself, and then we destroy it.
Now, if we fail an objective, uh, basically, we'll just do the exact same thing that we did here,
but instead, we'll get past the failed objective so we could do failed.
Objective name?
Which objective?
It's going to be a failed objective.
So we'll just pass failed OBJ here and then just copy this.
Paste it here, paste it here, paste it here.
Paste it here and then paste it here.
And instead we want to set the color equal to our red color.
All right.
So to display a small message on the screen, what we could do is we could get the text from ARGs and
we'll get past a key like text.
And then what we need to do here.
Is, since this small text label is going to be faded in and out, we need to first check if the small
message label dot text transparency is not equal to one.
So if our small message label is actually visible on the screen still, then we need to fade it out
before we can update the text on it.
So we're going to call our fade element function on our small message label.
We'll fade it out for like one second to make it completely transparent.
And then after this is finished yielding, then we can set the small message label dot text equal to
this text.
And then what we could do is play another sound which is inside of our G folder.
So this sound will play when we display a small message on the screen.
So we'll play it.
So that will let us know that.
Hey, a message just popped up.
So sound service dot g dot small message play.
And then now that we're playing this sound, we need to fade the small message back onto the screen
so we can call again our fade element function.
Pass our small message label.
We can again do something for like one second, and then we want to make it fully opaque.
And then after that, we can wait for another random amount of time.
We can wait for like five seconds.
So the message will be visible on our screen for a total of five seconds.
And before we fade it out, we need to verify that first our small message label dot transparency is
still equal to zero.
And we need to make sure that our small message label dot text is still equal to the original text that
was passed to our function.
Because let's say this message gets cut out by another message that the player interacts with, well,
we don't want to fade it out prematurely.
We only want to fade it out when we know that the transparency is still visible, and that the text
inside of this message label is still equal to the same text that we got passed originally.
After this wait statement is up.
If it is, then we're all clear to go ahead and fade our small message label back to be transparent.
The next function we can fill out is our display money frame function.
So again what we're going to get past is some text.
And we're going to get past, uh, in arguments.
We can get past a number from the server.
And then basically what we could do is we'll put a string here, we could do like plus, and then I'll
put a dollar sign and then we'll just concatenate this with a number.
So when we collect some money the server is going to tell us how much.
And then we'll display a string like uh plus dollar like maybe five bucks or something like that.
Whatever.
So we'll get past args dot number.
And then inside of our money frame there's a label in there called the amount label.
And we're going to set the text equal to this new text we just created.
We're going to set the money frame dot position equal to a random position on the screen.
So each time a player collects money, it's not always going to pop up on the same place on their screen.
So we can set the position equal to a new utum two.
And this is where we're going to use our RNG function to pick a random place.
So we'll do RNG next number for the X scale anywhere between 0.3 and 0.7.
We're going to do zero for the X offset.
And then for the Y scale we can do RNG next number anywhere between 0.2 and 0.9 scale on the screen
and then zero for the Y offset.
And let me just space this out so it looks nicer.
Now we can do money frame visibility equal to true.
And then what we need to do is we actually need to fade this money frame onto the screen.
So we need to spawn our fade element function in a new thread.
We're going to pass the money frame dot image label, and we're going to fade it in within 0.1 seconds.
And we're going to make it fully opaque.
And then what we're going to do is inside of the sound service and guy, there is a sound for when we
collect money, so collect money, we're going to play the sound and I'll give you a demo of it here
real quick.
So that will play when we collect money.
And then the other thing we need to fade onto the screen as well is the money frame dot amount label.
Because again, that's also going to be invisible.
We're also going to do 0.1 seconds and fade it to be opaque.
And then once that's done, we're going to use the tween service to create a new tween on our money
frame because we want to, uh, move this, uh, frame to slowly go upwards.
So it looks like it's animated and stuff like that makes it look nice.
So we could do tween info dot new.
Uh, we can move it up for something like 1.5 seconds.
And for the position, we want to set it equal to the current money frame dot position.
But we want to add a new offset.
So we'll add a new Utum two.
And we're going to move it up in the Y direction.
And in order to do that, we actually need to subtract on the Y scale.
So we could do like -0.1.
And then we'll just play this tween.
So it will cause our money frame to like slowly move up.
And while it's slowly moving up, we also want to fade it out at the exact same time.
So again we're going to spawn in a new thread our fade element.
We're going to pass the money frame dot image label.
And we also want to take 1.5 seconds and fade it to be completely transparent.
And then we're going to do the exact same thing for the amount label as well.
We're going to make it take 1.5 seconds and we're going to fade it to be completely transparent.
And that's all we need to do for this function as well.
Okay.
And the last function I think we can go ahead and fill out is our display died frame.
We're not going to have to worry about these functions for now, because this is going to be future
implementation that we'll be adding later in the course.
So inside our display died frame, what we could do is we could play inside of the sound service guy
dot died, and we'll play that sound and I'll show you what it is.
You probably heard of the sound before, but it's a popular meme sound for when somebody dies, and
I believe it was a Dark Souls game.
But anyways, we're going to play that sound.
We're going to set the visibility of our death frame equal to true.
And then we're going to go ahead and fade.
Um, we're going to fade our, uh, death frame.
And we'll fade it for like, point five seconds to be fully, uh, visible.
And then we want to do the same thing for our text as well.
So we'll spawn that.
Fade element function and a new thread will pass the death frame.
And there's a text label in there that says you died or whatever.
And again we'll have that take 0.5 seconds.
And then we'll have this be stuck displaying on the screen for three seconds, and then we're going
to fade it back out.
So we can basically just copy this.
And instead we'll set the transparency equal to one.
Alrighty, so that's pretty much all we're going to fill out for now.
Of course, this lecture is already probably getting too long, and we're going to continue working
on and scripting our game in the next lecture.
See you there.